package oss

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"
)

var (
	ErrLimitMaxKeys = errors.New("max keys should be lower than 1000")
)

func (o OssAPI) ListBucket(prefix string, marker string, maxKeys int) (*ListAllMyBucketsResult, error) {
	return o.GetService(prefix, marker, maxKeys)
}

// CreateBucket, bucket in BucketLocations, if empty, use default bucket
func (o OssAPI) CreateBucket(bucketLocation string, permission string) error {
	if bucketLocation == "" {
		bucketLocation = DefaultBucketLocation
	}

	if !isValidLocation(bucketLocation) {
		return errors.New(fmt.Sprintf("invalid bucket location %s, expect %v", bucketLocation, BucketLocations))
	}

	if permission == "" {
		permission = DefaultBucketPermission
	}
	if !isValidPermission(permission) {
		return errors.New(fmt.Sprintf("invalid permission %d, expect %v", permission, BucketPermissions))
	}

	bucketConf := CreateBucketConfiguration{
		LocationConstraint: bucketLocation,
	}

	header := http.Header{}
	header.Set("x-oss-acl", permission)

	return o.httpPut("/", nil, header, &bucketConf, nil)
}

func (o OssAPI) PutBucket(bucketLocation string, permission string) error {
	return o.CreateBucket(bucketLocation, permission)
}

// CreateBucketAcl, bucket in BucketLocations, if empty, use default bucket
func (o OssAPI) CreateBucketAcl(bucketLocation string, permission string) error {
	if bucketLocation == "" {
		bucketLocation = DefaultBucketLocation
	}

	if !isValidLocation(bucketLocation) {
		return errors.New(fmt.Sprintf("invalid bucket location %s, expect %v", bucketLocation, BucketLocations))
	}

	if permission == "" {
		permission = DefaultBucketPermission
	}
	if !isValidPermission(permission) {
		return errors.New(fmt.Sprintf("invalid permission %d, expect %v", permission, BucketPermissions))
	}

	bucketConf := CreateBucketConfiguration{
		LocationConstraint: bucketLocation,
	}

	header := http.Header{}
	header.Set("x-oss-acl", permission)

	respErr := &Error{}

	return o.httpPut("/?acl", nil, header, &bucketConf, respErr)
}

func (o OssAPI) PutBucketAcl(bucketLocation string, permission string) error {
	return o.CreateBucketAcl(bucketLocation, permission)
}

// SetBucketLogging,
func (o OssAPI) SetBucketLogging(enabled bool, targetBucket string, targetPrefix string) error {

	header := http.Header{}
	header.Set("Content-Type", "application/xml")
	header.Set("Date", getGmtDateOnNow())

	if enabled {
		status := &BucketLoggingStatus{
			LoggingEnabled: LoggingEnabled{
				TargetBucket: targetBucket,
				TargetPrefix: targetPrefix,
			},
		}
		return o.httpPut("/?logging", nil, header, &status, nil)

	} else {
		status := &BucketLoggingStatus{}
		return o.httpPut("/?logging", nil, header, &status, nil)
	}
	return nil
}

func (o OssAPI) PutBucketLogging(enabled bool, targetBucket string, targetPrefix string) error {
	return o.SetBucketLogging(enabled, targetBucket, targetPrefix)
}

func (o OssAPI) PutBucketWebsite(suffix string, errorDocument bool, key string) error {
	if suffix == "" || strings.Contains(suffix, "/") {
		return errors.New("suffix must not be empty and must not include a slash character")
	}

	header := http.Header{}
	header.Set("Content-Type", "application/xml")
	header.Set("Date", getGmtDateOnNow())

	conf := &WebsiteConfiguration{
		IndexDocument: IndexDocument{Suffix: suffix},
	}

	if errorDocument {
		conf.ErrorDocument = ErrorDocument{Key: key}
	}

	return o.httpPut("/?website", nil, header, &conf, nil)
}

func (o OssAPI) PutBucketReferrer(allowEmpty bool, referrers []string) error {
	header := http.Header{}
	header.Set("Content-Type", "application/xml")
	header.Set("Date", getGmtDateOnNow())

	conf := &ReferrerConfiguration{
		AllowEmptyReferrer: allowEmpty,
	}
	conf.RefererList.Referrer = referrers

	return o.httpPut("/?referer", nil, header, &conf, nil)
}

func (o OssAPI) PutBucketLifecycle(days int, date time.Time, ID string, prefix string, status bool) error {
	if !date.IsZero() && days > 0 {
		return errors.New("days or date exists one")
	}

	header := http.Header{}
	header.Set("Content-Type", "application/xml")
	header.Set("Date", getGmtDateOnNow())

	var ruleStatus string
	if status {
		ruleStatus = "Enabled"
	} else {
		ruleStatus = "Disabled"
	}

	r := Rule{
		Prefix: prefix,
		Status: ruleStatus,
		Expiration: Expiration{
			Days: days,
		},
	}
	rs := []Rule{r}

	conf := &LifecycleConfiguration{
		Rule: rs,
	}
	return o.httpPut("/?lifecycle", nil, header, &conf, nil)
}

func (o OssAPI) GetBucket(delimiter string, marker string, maxKeys int, prefix string, encodingType string) (*ListBucketResult, error) {
	v := url.Values{}

	if delimiter != "" {
		v.Set("delimiter", delimiter)
	}

	if marker != "" {
		v.Set("marker", marker)
	}

	if maxKeys != 0 {
		if maxKeys > 1000 {
			return nil, ErrLimitMaxKeys
		}
		v.Set("marker", fmt.Sprintf("%d", maxKeys))
	}

	if prefix != "" {
		v.Set("prefix", prefix)
	}

	if encodingType != "" {
		v.Set("encoding-type", encodingType)
	}

	bucket := ListBucketResult{}
	err := o.httpGet("/", v, nil, &bucket)
	if err != nil {
		return nil, err
	}
	return &bucket, nil
}

func (o OssAPI) GetBucketAcl() (*AccessControlPolicy, error) {
	policy := AccessControlPolicy{}
	err := o.httpGet("/?acl", nil, nil, &policy)
	if err != nil {
		return nil, err
	}
	return &policy, nil
}

func (o OssAPI) GetBucketLocation() (*LocationConstraint, error) {
	var loc LocationConstraint
	err := o.httpGet("/?location", nil, nil, &loc)
	if err != nil {
		return nil, err
	}
	return &loc, nil
}

func (o OssAPI) GetBucketLogging() (*BucketLoggingStatus, error) {
	status := BucketLoggingStatus{}
	err := o.httpGet("/?logging", nil, nil, &status)
	if err != nil {
		return nil, err
	}
	return &status, nil
}

func (o OssAPI) GetBucketWebsite() (*WebsiteConfiguration, error) {
	website := WebsiteConfiguration{}
	err := o.httpGet("/?website", nil, nil, &website)
	if err != nil {
		return nil, err
	}
	return &website, nil
}

func (o OssAPI) GetBucketReferrer() (*ReferrerConfiguration, error) {
	referrer := ReferrerConfiguration{}
	err := o.httpGet("/?referer", nil, nil, &referrer)
	if err != nil {
		return nil, err
	}
	return &referrer, nil
}

func (o OssAPI) GetBucketLifecycle() (*LifecycleConfiguration, error) {
	life := LifecycleConfiguration{}
	err := o.httpGet("/?lifecycle", nil, nil, &life)
	if err != nil {
		return nil, err
	}
	return &life, nil
}

// https://help.aliyun.com/document_detail/oss/api-reference/bucket/DeleteBucket.html?spm=5176.docoss/api-reference/bucket/GetBucketLifecycle.6.180.TKffmp
func (o OssAPI) DeleteBucket() error {
	return o.httpDelete("/", nil, nil, nil)
}

func (o OssAPI) DeleteBucketLogging() error {
	return o.httpDelete("/?logging", nil, nil, nil)
}

func (o OssAPI) DeleteBucketWebsite() error {
	return o.httpDelete("/?website", nil, nil, nil)
}

func (o OssAPI) DeleteBucketLifecycle() error {
	return o.httpDelete("/?lifecycle", nil, nil, nil)
}
